/**  
 * Copyrights@2006, Tianyuan DIC Information Co.,Ltd.
 * 文件名:    TaobaoFeeChannel.java  
 * 描述:   
 * 作者:     kaiyang
 * 版本:    1.0  
 * 创建时间:   2015年4月16日 上午10:50:15  
 *  
 * 修改历史:  
 * 日期                          作者           版本         描述  
 * ------------------------------------------------------------------  
 * 2015年4月16日      kaiyang      1.0        1.0 Version  
 */ 
package com.hp.bon.sgw.wh.channel;

import java.io.IOException;
import java.io.PrintStream;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.URL;
import java.security.PublicKey;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ThreadPoolExecutor;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.xml.security.signature.XMLSignature;
import org.apache.xml.security.utils.Constants;
import org.apache.xml.security.utils.XMLUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.simpleframework.http.Request;
import org.simpleframework.http.Response;
import org.simpleframework.http.core.Container;
import org.simpleframework.http.core.ContainerProcessor;
import org.simpleframework.http.core.ContainerSelector;
import org.simpleframework.http.core.ContainerServer;
import org.simpleframework.transport.connect.Connection;
import org.simpleframework.transport.connect.SocketConnection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alipay.standard.exception.MessageException;
import com.alipay.standard.model.message.ErrorCode;
import com.alipay.standard.model.message.MessageType;
import com.alipay.standard.model.message.StandardMessage;
import com.alipay.standard.model.message.body.BCReqMessageBody;
import com.alipay.standard.model.message.body.QBIReqMessageBody;
import com.alipay.standard.util.DocumentUtil;
import com.alipay.standard.util.KeyUtil;
import com.alipay.standard.util.SignUtil;
import com.alipay.standard.util.message.MessageHandle;
import com.hp.bon.sgw.Constants.NodeStatus;
import com.hp.bon.sgw.bean.XmlMessage;
import com.hp.bon.sgw.core.BuzProcessorManager;
import com.hp.bon.sgw.core.CallParam;
import com.hp.bon.sgw.core.channel.IChannel;
import com.hp.bon.sgw.domain.Node;
import com.hp.bon.sgw.domain.SGWChannel;
import com.hp.bon.sgw.service.ConfigListener;
import com.hp.bon.sgw.service.ConfigService;
import com.hp.bon.sgw.start.SpringContext;
import com.hp.bon.sgw.wh.Constants.AliParam;
import com.hp.bon.sgw.wh.channel.handler.TaobaoFeeHandle;
import com.hp.bon.sgw.wh.channel.handler.TaobaoPayHandler;
import com.hp.bon.sgw.wh.service.TaobaoResponseService;
import com.hp.bon.sgw.wh.service.WHConfigService;
import com.hp.ocs.diameter.DiameterUtils;
import com.hp.ocs.diameter.message.AVP_Time;
import com.sun.org.apache.xpath.internal.XPathAPI;

/**  
 * @ClassName: TaobaoFeeChannel   
 * @Description: TODO  
 * @author: kaiyang  
 * @date:2015年4月16日 上午10:50:15     
 */
public class TaobaoFeeChannel implements Container, ConfigListener, IChannel{

	private static final Logger _log = LoggerFactory.getLogger(TaobaoFeeChannel.class);
	private ContainerServer server = null;
	private SGWChannel channel;
	private MultiThreadedHttpConnectionManager connectionManager;
	private HttpClient httpClient;
	private ThreadPoolExecutor executor;
	private ThreadPoolExecutor collector;
	private static String TaoBaoPay = "taobaoPay";	
	private WHConfigService whConfigService;
	private ConfigService confSrv;
	private TaobaoResponseService taobaoResponseService;
	
	public TaobaoFeeChannel(SGWChannel channel) {
		this.channel = channel;
		confSrv = SpringContext.getInstance().getConfigService();
		whConfigService = SpringContext.getApplicationContext().getBean(WHConfigService.class);
		taobaoResponseService = SpringContext.getApplicationContext().getBean(TaobaoResponseService.class);		  
	}
	
	  
	@Override
	public void addPersist(Node arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void deletePersist(Node arg0) {
		// TODO Auto-generated method stub
		
	}

	/* (non-Javadoc)   
	 * @param arg0
	 * @return   
	 * @see com.hp.bon.sgw.core.channel.IChannel#getNodeSendQueueSize(java.lang.String)   
	 */  
	@Override
	public int getNodeSendQueueSize(String arg0) {
		// TODO Auto-generated method stub
		return 0;
	}

	/* (non-Javadoc)   
	 * @return   
	 * @see com.hp.bon.sgw.core.channel.IChannel#getSGWChannel()   
	 */  
	@Override
	public SGWChannel getSGWChannel() {
		return this.channel;
	}

	/* (non-Javadoc)   
	 * @return   
	 * @see com.hp.bon.sgw.core.channel.IChannel#getThreadPoolActiveCount()   
	 */  
	@Override
	public int getThreadPoolActiveCount() {
		// TODO Auto-generated method stub
		return 0;
	}

	/* (non-Javadoc)   
	 * @return   
	 * @see com.hp.bon.sgw.core.channel.IChannel#getThreadPoolQueueSize()   
	 */  
	@Override
	public int getThreadPoolQueueSize() {
		// TODO Auto-generated method stub
		return 0;
	}

	/* (non-Javadoc)   
	 * @return   
	 * @see com.hp.bon.sgw.core.channel.IChannel#getThreadPoolSize()   
	 */  
	@Override
	public int getThreadPoolSize() {
		// TODO Auto-generated method stub
		return 0;
	}

	/* (non-Javadoc)   
	 * @param arg0
	 * @return   
	 * @see com.hp.bon.sgw.core.channel.IChannel#sendRequest(com.hp.bon.sgw.core.CallParam)   
	 */  
	@Override
	public int sendRequest(CallParam arg0) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public void start() throws IOException {
		_log.info("start TaobaoFeeChannel:" + channel.toString() + " ...");
		String key = "sgw.taobaofeechannel.thread.max";
		int maxThreads = confSrv.getIntValue(key, 20);
		_log.info(key + " :" + maxThreads);
		String endPoint = this.channel.getUri();
		if (endPoint == null) {
			_log.warn("channel dont't defined uri");
			return;
		}
		_log.info("start sgw TaobaoFeeChannel server portal at " + endPoint);

		try {
			URL srvURL = new URL(endPoint);
			int port = srvURL.getPort();
			//找不到端口就启动失败
			if (port == -1) {
				_log.info("start TaobaoFeeChannel error : port Not defined");
				return ;
			}
			String host = srvURL.getHost();
			if (host.equals("127.0.0.1") || host.equalsIgnoreCase("localhost")) {
				host = "0.0.0.0";
			}
			server = new ContainerServer(this, maxThreads);

			Connection connection = new SocketConnection(server);
			SocketAddress address = new InetSocketAddress(host, port);
			connection.connect(address);
			collector = ((ContainerSelector) ((ContainerProcessor) server.getProcessor()).getSelector()).getCollect().getQueue();
			executor = ((ContainerSelector) ((ContainerProcessor) server.getProcessor()).getSelector()).getExecutor().getQueue();
			connectionManager = new MultiThreadedHttpConnectionManager();
			// ... configure connectionManager ...
			httpClient = new HttpClient(connectionManager);
			httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(10000);
			httpClient.getHttpConnectionManager().getParams().setSoTimeout(45000);

			this.channel.setStatus(NodeStatus.ON_LINE);
			confSrv.updateChannelStatus2DB(this.channel.getId(),NodeStatus.ON_LINE);
		} catch (IOException e) {
			_log.error("start TaobaoFeechannel " + this.channel.toString()+ " err " + e);
		}
	}

	public static void main(String[] args) throws Exception
	{
		AVP_Time time = new AVP_Time(0, 0, new Date(System.currentTimeMillis()));
		System.out.println(time);
	}
	
	@Override
	public void stop() {
		_log.info("stop BlackCard channel");
		if (server != null) {
			try {
				server.stop();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		if (connectionManager != null) {
			connectionManager.shutdown();
		}

		this.channel.setStatus(NodeStatus.OFF_LINE);
		confSrv.updateChannelStatus2DB(this.channel.getId(),NodeStatus.OFF_LINE);
		
		try {
			_log.info("wait 3s to exit...");
			Thread.sleep(3*1000);
		} catch (InterruptedException e) {
			_log.warn("Thread.sleep InterruptedException:",e);
		}
	}


	@Override
	public Set<String> monitorConfKeys() {
		// TODO Auto-generated method stub
		return null;
	}


	@Override
	public void onConfigUpdated(String arg0) {
		// TODO Auto-generated method stub
		
	}

	private void errorAck(PrintStream body,String type,String msgId,String errorCode,String errorMsg){
		try{
		    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
		    String msgTime = sdf.format(new Date());
		    Map<String,String> data = new HashMap<String, String>();
		    data.put("type",type);
		    data.put("msgId",msgId);
		    data.put("errorCode",errorCode);
		    data.put("errorMessage",errorMsg);
		    data.put("msgTime",msgTime);
			String priKeyPath = whConfigService.getValue("ali.priKeyPath","");
			String priKeyPassword = whConfigService.getValue("ali.priKeyPassword","");
		    String response = taobaoResponseService.getResponse("taobaoError",data,priKeyPath,priKeyPassword);
		    body.write(response.getBytes("GBK"));
		    body.close();
		}catch(Exception e){
			e.printStackTrace();
			_log.error("TaobaoFeeChannel errorAck error, msg = {}",e.getMessage());
		}
	}
	
    private MessageType getMessageType(String xml) throws Exception {
		Document doc = null;
		doc = DocumentHelper.parseText(xml);
		Element root = doc.getRootElement();
		org.dom4j.Node node = root.selectSingleNode("//message/head/type");
		String type = "";
		if (node != null)
		    type = node.getStringValue();
		MessageType messageType = MessageType.getMessageType(type);
		if (messageType == null)
		{
		    throw new MessageException(ErrorCode.MESSAGE_FORMAT_ERROR);
		}
		return messageType;        
    }

	@Override
	public void handle(Request request, Response response) {
		_log.info("TaobaoFeeChannel receive request from " + request.getClientAddress() + "; "	+ request.toString());
		String pubKeyPath = whConfigService.getValue("ali.pubKeyPath","");
		String bankCode = whConfigService.getValue(AliParam.bankCode,"ALIBB");
		String requestPath = request.getPath().getPath();		
		String IP = request.getClientAddress().getAddress().getHostAddress();
		long receiveTime = System.currentTimeMillis();
		PrintStream body = null;
		try {
			body = response.getPrintStream();
		} catch (IOException e) {
			e.printStackTrace();
		}
		response.set("Content-Type", "text/xml;charset=UTF-8");
		response.set("Server", "DIC Asyn Server )");
		response.setDate("Date", receiveTime);
		response.setDate("Last-Modified", receiveTime);
		try{
			String xml = request.getContent();
			_log.info("handle request msg :{}",xml);
		    
		    if(requestPath.endsWith(TaoBaoPay))
		    {
		    	_log.info("start taobaoPay......");
		    	//解包
		    	MessageType messageType = getMessageType(xml);
		    	_log.info("messageType = {}",messageType.toString());
		        String bodyXml = xml.substring(0,xml.indexOf("Signature")-1)+"</message>";
		        _log.info("bodyXml = {}",bodyXml);
		    	StandardMessage standardMessage = MessageHandle.unmarshal(bodyXml,messageType.getType());
		    	_log.info("开始签名校验......");
		    	_log.info("pubKeyPath = {}",pubKeyPath);
		    	//验证签名
	            boolean result = SignUtil.check(xml,pubKeyPath);
                _log.info("报文签名校验结果：" + result);
	            if (!result)
	            {
	            	errorAck(body,messageType.getType(),standardMessage.getMessageHeader().getMsgId(),"0005","");
	            }
				if (messageType == MessageType.QBIReq)//固话缴费查询
				{
					_log.info("QBIReq......");
					QBIReqMessageBody messageBody = (QBIReqMessageBody)standardMessage.getMessageBody();
					Element serviceInformation = DocumentHelper.createElement("Service_Information");
					//Bank_code
					Element element = serviceInformation.addElement("Bank_code");
					element.setText(bankCode);
					//Area_Code
					element = serviceInformation.addElement("Area_Code");
					element.setText(messageBody.getCompanyId());
					//Bill-Query
					Element elementBillQuery = serviceInformation.addElement("Bill_Query");
					//BillQuery-Type
					element = elementBillQuery.addElement("BillQuery_Type");
					element.setText("2");
					//Destination-Account
					element = elementBillQuery.addElement("Destination_Account");
					element.setText(messageBody.getBillKey());
					//Destination-Attr
					element = elementBillQuery.addElement("Destination_Attr");
					element.setText("0");
					//Query-Flag
					element = elementBillQuery.addElement("Query_Flag");
					element.setText("0");
					//Fee-Query-Flag
					element = elementBillQuery.addElement("Fee_Query_Flag");
					element.setText("0");
					XmlMessage req = new XmlMessage(true);
					String ALI_HOST = whConfigService.getValue(AliParam.aliSignHost,"Alibaba1@027.ctnbc-bon.net");
					String sessionId = DiameterUtils.makeSessionID(ALI_HOST);
					req.setIp(IP);
					req.setMsgTime(receiveTime);
					req.setMID(sessionId);
					req.setUID(ALI_HOST);
					req.setSCID("BillQueryByAcctID.Payment@027.ctnbc-bon.net");
					req.setSIG(null);
					req.setREPTAG(0);
					req.setDATA(serviceInformation.asXML());
					req.setDataElement(serviceInformation);
						
					// new CallParam对象
					TaobaoFeeHandle taobaoFeeHandle = new TaobaoFeeHandle(body, whConfigService, standardMessage, taobaoResponseService);
					CallParam callParam = new CallParam(req.getMID(), req.getSCID(),req, req.getUID(), taobaoFeeHandle);
					BuzProcessorManager.getInstance().processRequest(callParam);
				}
				else if (messageType == MessageType.BCReq)//固话缴费
				{  
					Element serviceInformation = DocumentHelper.createElement("Service_Information");
					BCReqMessageBody messageBody = (BCReqMessageBody)standardMessage.getMessageBody();
					//Bank_code
					Element element = serviceInformation.addElement("Bank_code");
					element.setText(bankCode);
					//Area_Code
					element = serviceInformation.addElement("Area_Code");
					element.setText(messageBody.getCompanyId());
					//Write_Off
					Element elementWriteOff = serviceInformation.addElement("Write_Off");
					//Acc_Nbr
					element = elementWriteOff.addElement("Acc_Nbr");
					element.setText(messageBody.getBillKey());
					//serv_id
					element = elementWriteOff.addElement("serv_id");
					element.setText("");
					//Acct_ID
					element = elementWriteOff.addElement("Acct_ID");
					element.setText(messageBody.getContractNo());
					//Latn_Id
					element = elementWriteOff.addElement("Latn_Id");
					element.setText("Latn_Id");
					//Payment_Amount
					element = elementWriteOff.addElement("Payment_Amount");
					element.setText(messageBody.getPayAmount()+"");
					//Payment_Method
					element = elementWriteOff.addElement("Payment_Method");
					element.setText("11");
					//Invoice_Offer
					element = elementWriteOff.addElement("Invoice_Offer");
					element.setText("0");
					//Paid_Time
					SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
					element = elementWriteOff.addElement("Paid_Time");
					element.setText(sdf.format(messageBody.getPayDate()));
					
					//Payment_Serial_Nbr
					element = elementWriteOff.addElement("Payment_Serial_Nbr");
					element.setText(messageBody.getBillNo());
					
					XmlMessage req = new XmlMessage(true);
					String ALI_HOST = whConfigService.getValue(AliParam.aliSignHost,"Alibaba1@027.ctnbc-bon.net");
					String sessionId = DiameterUtils.makeSessionID(ALI_HOST);
					req.setIp(IP);
					req.setMsgTime(receiveTime);
					req.setMID(sessionId);
					req.setUID(ALI_HOST);
					req.setSCID("NormalPay.Payment@027.ctnbc-bon.net");
					req.setSIG(null);
					req.setREPTAG(0);
					req.setDATA(serviceInformation.asXML());
					req.setDataElement(serviceInformation);
					
					// new CallParam对象
					TaobaoPayHandler taobaoPayHandle = new TaobaoPayHandler(body, whConfigService, standardMessage, taobaoResponseService);
					CallParam callParam = new CallParam(req.getMID(), req.getSCID(),req, req.getUID(), taobaoPayHandle);
					BuzProcessorManager.getInstance().processRequest(callParam);
				}
		    }
		}catch (Exception e) {
			e.printStackTrace();
			_log.warn(e.getMessage());
			errorAck(body,"","","3001","do business fail");
			return;
		}	
	}

}
